// app
#include "robot_def.h"
#include "robot_cmd.h"
// module
#include "remote_control.h"
#include "master_process.h"
#include "message_center.h"
#include "general_def.h"
#include "dji_motor.h"
#include "pump.h"
// bsp
#include <rm_referee.h>

#include "bsp_log.h"

#include "serialize.h"
#include "packets/XenoSlavePacket.h"

// 私有宏,自动将编码器转换成角度值
#define YAW_ALIGN_ANGLE (YAW_CHASSIS_ALIGN_ECD * ECD_ANGLE_COEF_DJI) // 对齐时的角度,0-360
#define PTICH_HORIZON_ANGLE (PITCH_HORIZON_ECD * ECD_ANGLE_COEF_DJI) // pitch水平时电机的角度,0-360


static Publisher_t *chassis_cmd_pub; // 底盘控制消息发布者
static Subscriber_t *chassis_feed_sub; // 底盘反馈信息订阅者// ONE_BOARD

static Chassis_Ctrl_Cmd_s chassis_cmd_send; // 发送给底盘应用的信息,包括控制信息和UI绘制相关
static Chassis_Upload_Data_s chassis_fetch_data; // 从底盘应用接收的反馈信息信息,底盘功率枪口热量与底盘运动状态等

static RC_ctrl_t *rc_data; // 遥控器数据,初始化时返回

static referee_info_t *referee_recv_info; // 接收到的裁判系统数据
static XenoMasterPacket *vision_recv_data; // 视觉接收数据指针,初始化时返回
static XenoSlavePacket slave_packet; // 视觉发送数据
static uint8_t vision_send_buffer[33];

static Publisher_t *gimbal_cmd_pub; // 云台控制消息发布者
static Subscriber_t *gimbal_feed_sub; // 云台反馈信息订阅者
static Gimbal_Ctrl_Cmd_s gimbal_cmd_send; // 传递给云台的控制信息
static Gimbal_Upload_Data_s gimbal_fetch_data; // 从云台获取的反馈信息

static Robot_Status_e robot_state; // 机器人整体工作状态

void generateSlavePacket() {
    auto const data = &referee_recv_info->controller_data;
    slave_packet.r1 = data->r1;
    slave_packet.r2 = data->r2;
    slave_packet.r3 = data->r3;
    slave_packet.lift = 0;
    slave_packet.stretch = 0;
    slave_packet.shift = 0;
}

void RobotCMDInit() {
    rc_data = RemoteControlInit(&huart3); // 修改为对应串口,注意如果是自研板dbus协议串口需选用添加了反相器的那个
    referee_recv_info = RefereeInit(&huart6); // 初始化裁判系统的串口,并返回裁判系统反馈数据指针
    referee_recv_info->init_flag = 1;
    vision_recv_data = VisionInit(&huart1); // 视觉通信串口

    gimbal_cmd_pub = PubRegister("gimbal_cmd", sizeof(Gimbal_Ctrl_Cmd_s));
    gimbal_feed_sub = SubRegister("gimbal_feed", sizeof(Gimbal_Upload_Data_s));

    gimbal_cmd_send.pitch = 45;
    gimbal_cmd_send.yaw = -100;
    gimbal_cmd_send.lift = 0;

    chassis_cmd_pub = PubRegister("chassis_cmd", sizeof(Chassis_Ctrl_Cmd_s));
    chassis_feed_sub = SubRegister("chassis_feed", sizeof(Chassis_Upload_Data_s));

    init_xeno_slave_packet(&slave_packet);

    robot_state = ROBOT_READY; // 启动时机器人进入工作模式,后续加入所有应用初始化完成之后再进入
}

/**
 * @brief 控制输入为遥控器(调试时)的模式和控制量设置
 *
 */
static void RemoteControlSet() {
    if (switch_is_down(rc_data[TEMP].rc.switch_right)) // 右侧开关状态[下],底盘跟随云台
    {
        chassis_cmd_send.chassis_mode = CHASSIS_ROTATE;
    } else if (switch_is_mid(rc_data[TEMP].rc.switch_right)) // 右侧开关状态[中],底盘和云台分离,底盘保持不转动
    {
        chassis_cmd_send.chassis_mode = CHASSIS_NO_FOLLOW;
        gimbal_cmd_send.gimbal_mode = GIMBAL_FREE_MODE;
    }

    // 云台参数,确定云台控制数据
    if (switch_is_mid(rc_data[TEMP].rc.switch_left)) // 左侧开关状态为[中],视觉模式
    {
        // 待添加,视觉会发来和目标的误差,同样将其转化为total angle的增量进行控制
        // ...
    }
    // 左侧开关状态为[下],或视觉未识别到目标,纯遥控器拨杆控制
    if (switch_is_down(rc_data[TEMP].rc.switch_left)) {
        // 按照摇杆的输出大小进行角度增量,增益系数需调整
        gimbal_cmd_send.yaw += (float) rc_data->rc.rocker_l_ / 660.0f;
        gimbal_cmd_send.pitch -= (float) rc_data->rc.rocker_l1 / 660.0f;
        gimbal_cmd_send.lift -= (float) rc_data->rc.dial / 660.0f * 10.f;
    }
    // 云台软件限位

    // 底盘参数,目前没有加入小陀螺(调试似乎暂时没有必要),系数需要调整
    chassis_cmd_send.vx = 10.0f * (float) rc_data[TEMP].rc.rocker_r_; // _水平方向
    chassis_cmd_send.vy = 10.0f * (float) rc_data[TEMP].rc.rocker_r1; // 1数值方向
}

static void ArmRemoteControlSet() {
    if (switch_is_down(rc_data[TEMP].rc.switch_right)) // 右侧开关状态[下],控制升降、伸出、横移；
    {
        slave_packet.lift -= (float) rc_data->rc.dial / 660.0f * 10.0f;
        slave_packet.stretch += (float) rc_data->rc.rocker_l_ / 660.0f;
        slave_packet.shift += (float) rc_data->rc.rocker_l_ / 660.0f;
    } else if (switch_is_mid(rc_data[TEMP].rc.switch_right)) // 右侧开关状态[中],控制R1、R2、R3、吸盘转动、吸盘吸力
    {
        slave_packet.r1 += (float) rc_data->rc.rocker_l_ / 660.0f / 10.0f;
        slave_packet.r2 += (float) rc_data->rc.rocker_l1 / 660.0f / 10.0f;
        slave_packet.r3 += (float) rc_data->rc.rocker_r_ / 660.0f / 10.0f;
        slave_packet.suck_rotate += (float) rc_data->rc.rocker_r1 / 660.0f / 10.0f;
        if ((float) rc_data->rc.dial > 1.f) {
            pumpSetSuction(1.0f);
        } else {
            pumpSetSuction(0.0f);
        }
    }
}

/**
 * @brief 输入为键鼠时模式和控制量设置
 *
 */
static void MouseKeySet() {
    //W/A/S/D 底盘移动
    chassis_cmd_send.vx = rc_data[TEMP].key[KEY_PRESS].w * 300 - rc_data[TEMP].key[KEY_PRESS].s * 300; // 系数待测
    // chassis_cmd_send.vy = rc_data[TEMP].key[KEY_PRESS].s * 300 - rc_data[TEMP].key[KEY_PRESS].d * 300;
    chassis_cmd_send.vy = rc_data[TEMP].key[KEY_PRESS].a * 300 - rc_data[TEMP].key[KEY_PRESS].d * 300;
    //鼠标 云台移动 E/Q控制云台升降
    gimbal_cmd_send.yaw += (float) rc_data[TEMP].mouse.x / 660 * 10; // 系数待测
    gimbal_cmd_send.pitch += (float) rc_data[TEMP].mouse.y / 660 * 10;
    gimbal_cmd_send.lift += (float) rc_data[TEMP].key[KEY_PRESS].e * 10 - (float) rc_data[TEMP].key[KEY_PRESS].q * 10;
    // F/V控制伸出， C/B控制横移
    slave_packet.stretch += (float) rc_data[TEMP].key[KEY_PRESS].f * 10 - (float) rc_data[TEMP].key[KEY_PRESS].v * 10;
    slave_packet.shift += (float) rc_data[TEMP].key[KEY_PRESS].c * 10 - (float) rc_data[TEMP].key[KEY_PRESS].b * 10;
    // Shift/Ctrl控制升降
    slave_packet.lift += (float) rc_data[TEMP].key[KEY_PRESS].ctrl * 10 - (float) rc_data[TEMP].key[KEY_PRESS].shift *
            10;
    //使用自定义控制器数据填充机械臂角度
    slave_packet.r1 = referee_recv_info->controller_data.r1;
    slave_packet.r2 = referee_recv_info->controller_data.r2;
    slave_packet.r3 = referee_recv_info->controller_data.r3;
    //R/G控制吸盘顺/逆时针转动
    slave_packet.suck_rotate += (float) rc_data[TEMP].key[KEY_PRESS].r * 10 - (float) rc_data[TEMP].key[KEY_PRESS].g;
    //Z/X控制泵开闭
    if (rc_data->key[KEY_PRESS].z) {
        pumpSetSuction(1.f);
    } else if (rc_data->key[KEY_PRESS].x) {
        pumpSetSuction(0.f);
    }
}

/**
 * @brief  紧急停止,包括遥控器左开关状态为[上]/重要模块离线
 *
 *
 */
static bool EmergencyHandler() {
    if (switch_is_up(rc_data[TEMP].rc.switch_right) || robot_state == ROBOT_STOP) // 还需添加重要应用和模块离线的判断
    {
        robot_state = ROBOT_STOP;
        gimbal_cmd_send.gimbal_mode = GIMBAL_ZERO_FORCE;
        chassis_cmd_send.chassis_mode = CHASSIS_ZERO_FORCE;
        LOGERROR("[CMD] emergency stop!");
        return 1;
    }
    // 遥控器右侧开关不为[上],恢复正常运行
    if (!switch_is_up(rc_data[TEMP].rc.switch_right) || robot_state == ROBOT_STOP) {
        robot_state = ROBOT_READY;
        LOGINFO("[CMD] reinstate, robot ready");
        return 0;
    }
    return 0;
}

/* 机器人核心控制任务,200Hz频率运行(必须高于视觉发送频率) */
void RobotCMDTask() {
    SubGetMessage(chassis_feed_sub, (void *) &chassis_fetch_data);

    SubGetMessage(gimbal_feed_sub, &gimbal_fetch_data);

    // 根据遥控器左侧开关,确定当前使用的控制模式为遥控器调试还是键鼠
    if (switch_is_down(rc_data[TEMP].rc.switch_left)) // 遥控器左侧开关状态为[下],遥控器控制
        RemoteControlSet();
    else if (switch_is_up(rc_data[TEMP].rc.switch_left)) // 遥控器左侧开关状态为[上],键盘控制
        MouseKeySet();
    else {
        //左侧开关为[中]，遥控器控制机械臂
        ArmRemoteControlSet();
    }

    if (!EmergencyHandler()) {
        // 处理模块离线和遥控器急停等紧急情况，不发送数据给上位机
        serialize(&slave_packet, vision_send_buffer, 33);
        VisionSend(vision_send_buffer, 33);
    }
    // 其他应用所需的控制数据在remotecontrolsetmode和mousekeysetmode中完成设置
    PubPushMessage(chassis_cmd_pub, (void *) &chassis_cmd_send);
    PubPushMessage(gimbal_cmd_pub, (void *) &gimbal_cmd_send);
}
